package redis

import (
	"encoding/json"
	"fmt"
	"github.com/edgexfoundry/edgex-go/internal/app/models"
	pkgCommon "github.com/edgexfoundry/edgex-go/internal/pkg/common"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/errors"
	"github.com/gomodule/redigo/redis"
)

const (
	ProductCollection      = "md|pd"
	ProductCollectionName  = ProductCollection + DBKeySeparator + "name"
	ProductCollectionGroup = ProductCollection + DBKeySeparator + "group"
)

func productStoreKey(id string) string {
	return CreateKey(ProductCollection, id)
}

// 检查productId是否存在
func productIdExists(conn redis.Conn, id string) (bool, errors.EdgeX) {
	exists, err := objectIdExists(conn, productStoreKey(id))
	if err != nil {
		return false, errors.NewCommonEdgeX(errors.KindDatabaseError, "product existence check by id failed", err)
	}
	return exists, nil
}

func productNameExists(conn redis.Conn, name string) (bool, errors.EdgeX) {
	exists, err := objectNameExists(conn, ProductCollectionName, name)
	if err != nil {
		return false, errors.NewCommonEdgeX(errors.KindDatabaseError, "product existence check by name failed", err)
	}
	return exists, nil
}

// 向redis中存储产品
func sendAddProductCmd(conn redis.Conn, storeKey string, p models.Product) errors.EdgeX {
	dsJSONBytes, err := json.Marshal(p)
	if err != nil {
		return errors.NewCommonEdgeX(errors.KindContractInvalid, "unable to JSON marshal product for Redis persistence", err)
	}
	// md|pd:产品id
	_ = conn.Send(SET, storeKey, dsJSONBytes)
	// md|pd
	_ = conn.Send(ZADD, ProductCollection, 0, storeKey)
	// md|pd:name
	_ = conn.Send(HSET, ProductCollectionName, p.ProductName, storeKey)
	// md:pd:产品分组id:group:产品名称:name  用来模糊查询,
	_ = conn.Send(SET, CreateKey(ProductCollectionGroup, p.ProductOrganId, p.ProductName, "name"), storeKey)
	// md:pd:group:分组id
	_ = conn.Send(ZADD, CreateKey(ProductCollectionGroup, p.ProductOrganId), 0, storeKey)
	return nil
}

// 从redis中删除产品
func sendDeleteProductCmd(conn redis.Conn, storeKey string, p models.Product) errors.EdgeX {
	// 删除md|pd:产品id
	_ = conn.Send(DEL, storeKey)
	// 删除md|pd中的产品key
	_ = conn.Send(ZREM, ProductCollection, storeKey)
	// 删除md|pd:name中的该产品名称
	_ = conn.Send(HDEL, ProductCollectionName, p.ProductName)
	// md|pd:产品名称:name  用来模糊查询
	_ = conn.Send(DEL, CreateKey(ProductCollectionGroup, p.ProductOrganId, p.ProductName, "name"))
	// 删除md:pd:group:分组id中的key
	_ = conn.Send(ZREM, CreateKey(ProductCollectionGroup, p.ProductOrganId), storeKey)
	return nil
}

func addProduct(conn redis.Conn, p models.Product) (models.Product, errors.EdgeX) {
	// 检查productId是否存在
	exists, edgeXerr := productIdExists(conn, p.Id)
	if edgeXerr != nil {
		return p, errors.NewCommonEdgeXWrapper(edgeXerr)
	} else if exists {
		return p, errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("product id %s already exists", p.Id), edgeXerr)
	}

	// 检查productName是否存在
	exists, edgeXerr = productNameExists(conn, p.ProductName)
	if edgeXerr != nil {
		return p, errors.NewCommonEdgeXWrapper(edgeXerr)
	} else if exists {
		return p, errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("product name %s already exists", p.ProductName), edgeXerr)
	}

	ts := pkgCommon.MakeTimestamp()
	if p.CreateTime == 0 {
		p.CreateTime = ts
	}

	storeKey := productStoreKey(p.Id)
	// 标记事务开始
	_ = conn.Send(MULTI)
	edgeXerr = sendAddProductCmd(conn, storeKey, p)
	if edgeXerr != nil {
		return p, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	// 事务结束
	_, err := conn.Do(EXEC)
	if err != nil {
		edgeXerr = errors.NewCommonEdgeX(errors.KindDatabaseError, "product creation failed", err)
	}
	return p, edgeXerr
}

// productsByLabels 分页查询产品列表
func allProducts(conn redis.Conn, offset int, limit int, groupId string) (products []models.Product, edgeXerr errors.EdgeX) {
	var objects [][]byte
	if groupId != "" {
		// 从该分组分页查询产品
		objects, edgeXerr = getObjectsByRange(conn, CreateKey(ProductCollectionGroup, groupId), offset, limit)
		if edgeXerr != nil {
			return products, errors.NewCommonEdgeXWrapper(edgeXerr)
		}
	} else {
		// 从所有分组分页查询产品
		objects, edgeXerr = getObjectsByRange(conn, ProductCollection, offset, limit)
		if edgeXerr != nil {
			return products, errors.NewCommonEdgeXWrapper(edgeXerr)
		}
	}
	// 创建返回产品的数组
	products = make([]models.Product, len(objects))
	for i, in := range objects {
		p := models.Product{}
		err := json.Unmarshal(in, &p)
		if err != nil {
			return []models.Product{}, errors.NewCommonEdgeX(errors.KindDatabaseError, "product format parsing failed from the database", err)
		}
		products[i] = p
		// 查询该产品的设备数量
		number, err := getMemberNumber(conn, ZCARD, CreateKey(ProductDeviceCollection, p.Id))
		if err != nil {
			continue
		}
		products[i].DeviceNum = number
	}
	return products, nil
}

/**
 * @Description 查询产品信息-通过是否show
 * @method
 * @auth kely
 * @Date ${DATE}
 * @Param
 * @return
 **/

func OwnProducts(conn redis.Conn) (products []models.Product, edgeXerr errors.EdgeX) {
	// 从redis中查询产品列表
	objects, edgeXerr := getObjectsByRange(conn, ProductCollection, 0, -1)
	if edgeXerr != nil {
		return products, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	// 创建返回产品的数组
	products = make([]models.Product, len(objects))
	var productsCopy []models.Product // 创建新的数组接收过滤后的数据
	for i, in := range objects {
		p := models.Product{}
		err := json.Unmarshal(in, &p)
		if err != nil {
			return []models.Product{}, errors.NewCommonEdgeX(errors.KindDatabaseError, "product format parsing failed from the database", err)
		}
		if p.IsPublished == true {
			productsCopy = append(productsCopy, p) // 接受数据
		}
		products[i] = p
	}
	return productsCopy, nil
}

// productByName 通过产品名称查询产品
//func productByName(conn redis.Conn, name string) (product models.Product, edgeXerr errors.EdgeX) {
//	edgeXerr = getObjectByHash(conn, ProductCollectionName, name, &product)
//	// 查询该产品的设备数量
//	number, err := getMemberNumber(conn, ZCARD, CreateKey(ProductDeviceCollection, product.Id))
//	if err != nil {
//		return product, errors.NewCommonEdgeXWrapper(err)
//	}
//	product.DeviceNum = number
//	if edgeXerr != nil {
//		return product, errors.NewCommonEdgeXWrapper(edgeXerr)
//	}
//	return
//}

// productsByName 通过名称模糊查询产品
func productsByName(conn redis.Conn, offset int, limit int, name string, groupId string) (products []models.Product, totalCount uint32, edgeXerr errors.EdgeX) {
	name = "*" + name + "*"
	var storeKey string
	if groupId != "" {
		// md|pd:group:产品分组id:*产品名称*:name   通过产品名称模糊查询某分组的产品
		storeKey = CreateKey(ProductCollectionGroup, groupId, name, "name")
	} else {
		// md|pd:group:*产品名称*:name    通过产品名称模糊查询所有分组的产品
		storeKey = CreateKey(ProductCollectionGroup, name, "name")
	}
	// 得到包含该名称的key
	objs, err := redis.ByteSlices(conn.Do("keys", storeKey))
	if err != nil {
		return products, 0, errors.NewCommonEdgeXWrapper(err)
	}
	// 得到包含该名称的key,分页
	var keys []interface{}
	for _, v := range objs {
		keys = append(keys, v)
	}
	l := len(objs)
	start := offset
	if start > l {
		return products, uint32(l), nil
	}
	end := offset + limit
	if end > l {
		end = l
	}
	// 通过包含该名称的key，查询产品id
	objects, err := getObjectsByIds(conn, keys[start:end])
	if err != nil {
		return products, uint32(l), errors.NewCommonEdgeXWrapper(err)
	}

	// 通过产品id查询产品
	var keysid []interface{}
	for _, v := range objects {
		keysid = append(keysid, v)
	}
	// 通过产品id查询产品
	objectsByIds, err := getObjectsByIds(conn, keysid)
	if err != nil {
		return products, uint32(l), errors.NewCommonEdgeXWrapper(err)
	}

	products = make([]models.Product, len(objectsByIds))
	for i, v := range objectsByIds {
		var product models.Product
		err := json.Unmarshal(v, &product)
		if err != nil {
			continue
		}
		products[i] = product
		// 查询该产品的设备数量
		number, err := getMemberNumber(conn, ZCARD, CreateKey(ProductDeviceCollection, product.Id))
		if err != nil {
			continue
		}
		products[i].DeviceNum = number
	}
	return products, uint32(l), nil
}

// productById 通过产品id查询产品
func productById(conn redis.Conn, id string) (product models.Product, edgeXerr errors.EdgeX) {
	edgeXerr = getObjectById(conn, CreateKey(ProductCollection, id), &product)
	if edgeXerr != nil {
		// 查询该产品的设备数量
		number, err := getMemberNumber(conn, ZCARD, CreateKey(ProductDeviceCollection, product.Id))
		if err != nil {
			return product, errors.NewCommonEdgeXWrapper(err)
		}
		product.DeviceNum = number
		return product, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return
}

// productDelete product delete
func productDelete(conn redis.Conn, productId string) errors.EdgeX {
	product, err := productById(conn, productId)
	if err != nil {
		return errors.NewCommonEdgeXWrapper(err)
	}
	productKey := CreateKey(ProductCollection, productId)
	_ = conn.Send(MULTI)
	err = sendDeleteProductCmd(conn, productKey, product)
	if err != nil {
		return errors.NewCommonEdgeXWrapper(err)
	}
	_, err2 := conn.Do(EXEC)
	if err2 != nil {
		return errors.NewCommonEdgeX(errors.KindDatabaseError, "产品删除失败", err2)
	}
	return nil
}

// ProductUpdate product update
func ProductUpdate(conn redis.Conn, p models.Product) (edgeXerr errors.EdgeX) {
	var oldProduct models.Product
	oldProduct, edgeXerr = productById(conn, p.Id)
	if edgeXerr != nil {
		return errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	p.CreateTime = oldProduct.CreateTime
	if oldProduct.ProductName != p.ProductName {
		exists, edgeXerr := productNameExists(conn, p.ProductName)
		if edgeXerr != nil {
			return errors.NewCommonEdgeXWrapper(edgeXerr)
		} else if exists {
			return errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("product name %s already exists", p.ProductName), edgeXerr)
		}
	}

	// 事务开始
	_ = conn.Send(MULTI)
	storeKey := productStoreKey(p.Id)
	sendDeleteProductCmd(conn, storeKey, oldProduct)
	edgeXerr = sendAddProductCmd(conn, storeKey, p)
	if edgeXerr != nil {
		return errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	// 事务结束
	_, err := conn.Do(EXEC)
	if err != nil {
		return errors.NewCommonEdgeX(errors.KindDatabaseError, "product update failed", err)
	}
	return nil
}

// 通过产品分类id查询该分类产品
//func ProductsByGroupId(conn redis.Conn, groupId string, offset int, limit int) (products []models.Product, edgeXerr errors.EdgeX) {
//	objectsByRange, edgeXerr := getObjectsByRange(conn, CreateKey(ProductCollectionGroup, groupId), offset, limit)
//	if edgeXerr != nil {
//		return products, errors.NewCommonEdgeXWrapper(edgeXerr)
//	}
//	products = make([]models.Product, len(objectsByRange))
//	for i, v := range objectsByRange {
//		p := models.Product{}
//		err := json.Unmarshal(v, &p)
//		if err != nil {
//			continue
//		}
//		products[i] = p
//	}
//	return products, nil
//}

// allProductsWithDEviceNum 分页查询产品列表,带该产品的设备数量
//func allProductsWithDEviceNum(conn redis.Conn, offset int, limit int) (products []models.Product, edgeXerr errors.EdgeX) {
//	// 从redis中查询产品列表
//	objects, edgeXerr := getObjectsByRange(conn, ProductCollection, offset, limit)
//	if edgeXerr != nil {
//		return products, errors.NewCommonEdgeXWrapper(edgeXerr)
//	}
//	// 创建返回产品的数组
//	products = make([]models.Product, len(objects))
//	for i, in := range objects {
//		p := models.Product{}
//		err := json.Unmarshal(in, &p)
//		if err != nil {
//			return []models.Product{}, errors.NewCommonEdgeX(errors.KindDatabaseError, "product format parsing failed from the database", err)
//		}
//		products[i] = p
//		number, err := getMemberNumber(conn, ZCARD, CreateKey(ProductDeviceCollection, p.Id))
//		if err != nil {
//			continue
//		}
//		products[i].DeviceNum = number
//	}
//	return products, nil
//}
